package com.ma.chapter5.note;

import lombok.extern.slf4j.Slf4j;

import java.io.Serializable;

/* 实现2 */
// 问题1：枚举单例是如何限制实例个数的
//    ---> 本质就是枚举类内部的静态成员变量，单实例
// 问题2：枚举单例在创建时是否有并发问题
//    ---> 类加载阶段，JVM保证其创建过程线程安全
// 问题3：枚举单例能否被反射破坏单例
//    ---> 不能，反射不能得到枚举类
// 问题4：枚举单例能否被反序列化破坏单例
//    ---> 不能，设计 枚举类的反序列化通过ValueOf()实现
// 问题5：枚举单例属于懒汉式还是饿汉式
//    ---> 饿汉式，初始为null
// 问题6：枚举单例如果希望加入一些单例创建时的初始化逻辑该如何做
//    ---> 添加构造方法即可
enum Singleton2 {
    INSTANCE;
}

/**
 * @ClassName ThreadSafeSinglonProblem
 * @Author: mayongqiang
 * @DATE 2022/3/18 23:51
 * @Description: 线程安全单例问题
 */
/*
单例模式有很多实现方法，饿汉、懒汉、静态内部类、枚举类，试分析每种实现下获取单例对象
                        （即调用getInstance）时的线程安全，并思考注释中的问题
    饿汉式：类加载就会导致该单实例对象被创建
    懒汉式：类加载不会导致该单实例对象被创建，而是首次使用该对象时才会创建
 */
@Slf4j(topic = "m.ThreadSafeSinglonProblem")
public class ThreadSafeSingletonProblem {

}

/* 实现 1 */
//问题1、为什么加final ------> 防止子类继承重写方法破坏单例
//问题2、如果实现了序列化接口,还要做什么来防止反序列化破坏单例
//    ------> 重写readResolve() 当调用反序列化创建对象时，原理是调用readResolve方法，重写它使其返回单例即可
final class Singleton1 implements Serializable {
    // 问题4：这样初始化是否能保证单例对象创建时的线程安全?
    //  --->静态成员变量初始化在类加载<cl init>阶段，由JVM保证线程安全！
    private static final Singleton1 INSTANCE = new Singleton1();

    //问题3、构造器为什么设置为私有的？（禁止被外部调用）是否能防止反射创建新的实例?（不能）
    private Singleton1() {
    }

    // 问题5：为什么提供静态方法而不是直接将 INSTANCE 设置为 public, 说出你知道的理由
    //---> 提供更好的封装性,提供懒惰初始化；
    public static Singleton1 getInstance() {
        return INSTANCE;
    }

    public Object readResolve() {
        return INSTANCE;
    }
}

/* 实现3 懒汉式单例 */
final class Singleton3 {
    private static Singleton3 INSTANCE = null;

    private Singleton3() {
    }

    // 分析这里的线程安全, 并说明有什么缺点
    //--> 锁定范围大。第一次调用加锁，第二次/第三次调用对象都创建好了仍然要加锁，性能低
    public static synchronized Singleton3 getInstance() {
        if (INSTANCE != null) {
            return INSTANCE;
        }
        INSTANCE = new Singleton3();
        return INSTANCE;
    }
}

//对懒汉式做改进，锁范围缩小。
/* 实现4 DCL double-checked locking */
final class Singleton4 {
    // 问题1：解释为什么要加 volatile ?
    //---> 避免构造方法指令重排序
    private static volatile Singleton4 INSTANCE = null;

    private Singleton4() {
    }

    // 问题2：对比实现3, 说出这样做的意义
    // ---> 后续调用不用加锁直接获得单例，性能相较3优越
    public static Singleton4 getInstance() {
        if (INSTANCE != null) {
            return INSTANCE;
        }
        synchronized (Singleton4.class) {
            // 问题3：为什么还要在这里加为空判断, 之前不是判断过了吗
            //----！防止首次创建INSTANCE时的线程安全问题，不加的化第一次并发访问时可能被重复创建
            if (INSTANCE != null) { // t2
                return INSTANCE;
            }
            INSTANCE = new Singleton4();
            return INSTANCE;
        }
    }
}

/* 实现5 静态内部类！ */
final class Singleton5 {
    private Singleton5() {
    }

    // 问题2：在创建时是否有并发问题
    // 第一次调用方法，用到LazyHolder,触发类加载，JVM保证静态变量线程安全
    public static Singleton5 getInstance() {
        return LazyHolder.INSTANCE;
    }

    // 问题1：属于懒汉式还是饿汉式
    /* 静态内部类！ 类加载就是懒汉式的 JVM保证线程安全 */
    private static class LazyHolder {
        static final Singleton5 INSTANCE = new Singleton5();
    }
}

